#!/bin/bash
# OpenBMC库文件提取脚本
# 功能：从Yocto构建目录中提取编译好的.so文件和头文件
# 使用方法：在Docker容器内运行此脚本

set -e  # 遇到错误立即退出
set -u  # 使用未定义变量时报错

# ============================================================================
# 配置区域
# ============================================================================

# OpenBMC源码目录
OPENBMC_SOURCE="${OPENBMC_SOURCE:-/workspace/data/openbmc_source}"
# 构建输出目录
BUILD_OUTPUT="${BUILD_OUTPUT:-/workspace/data/build}"
# 库文件输出目录
LIBRARY_OUTPUT="$BUILD_OUTPUT/libraries"
# 头文件输出目录
HEADER_OUTPUT="$BUILD_OUTPUT/headers"
# 库清单文件
INVENTORY_FILE="$BUILD_OUTPUT/library_inventory.txt"

# Yocto工作目录
YOCTO_WORK_DIR="$OPENBMC_SOURCE/build/tmp/work"
YOCTO_DEPLOY_DIR="$OPENBMC_SOURCE/build/tmp/deploy"

# ============================================================================
# 函数定义
# ============================================================================

# 日志函数
log_info() {
    echo "[INFO] $(date '+%Y-%m-%d %H:%M:%S') - $*"
}

log_error() {
    echo "[ERROR] $(date '+%Y-%m-%d %H:%M:%S') - $*" >&2
}

log_success() {
    echo "[SUCCESS] $(date '+%Y-%m-%d %H:%M:%S') - $*"
}

# 创建必要的目录
prepare_directories() {
    log_info "准备输出目录..."
    mkdir -p "$LIBRARY_OUTPUT"
    mkdir -p "$HEADER_OUTPUT"
    log_success "输出目录准备完成"
}

# 查找并提取.so文件
extract_so_files() {
    log_info "开始搜索.so文件..."
    
    local so_count=0
    
    # 搜索多个可能的目录
    local search_paths=(
        "$YOCTO_WORK_DIR"
        "$YOCTO_DEPLOY_DIR"
    )
    
    for search_path in "${search_paths[@]}"; do
        if [ ! -d "$search_path" ]; then
            log_info "跳过不存在的目录: $search_path"
            continue
        fi
        
        log_info "在目录中搜索: $search_path"
        
        # 查找所有.so和.so.*文件
        while IFS= read -r -d '' so_file; do
            # 跳过符号链接和.so.debug文件
            if [ -L "$so_file" ] || [[ "$so_file" == *".debug"* ]]; then
                continue
            fi
            
            # 获取文件名
            local filename=$(basename "$so_file")
            
            # 复制到输出目录
            if cp "$so_file" "$LIBRARY_OUTPUT/$filename" 2>/dev/null; then
                log_info "提取: $filename"
                ((so_count++))
            fi
        done < <(find "$search_path" -type f \( -name "*.so" -o -name "*.so.*" \) -print0 2>/dev/null)
    done
    
    log_success "共提取 $so_count 个.so文件"
    return 0
}

# 提取头文件
extract_header_files() {
    log_info "开始搜索头文件..."
    
    local header_count=0
    
    if [ ! -d "$YOCTO_WORK_DIR" ]; then
        log_error "Yocto工作目录不存在: $YOCTO_WORK_DIR"
        return 1
    fi
    
    # 查找特定库的头文件目录
    local libraries=(
        "sdbusplus"
        "libpldm"
        "phosphor-logging"
        "phosphor-dbus-interfaces"
        "gpioplus"
        "stdplus"
    )
    
    for lib in "${libraries[@]}"; do
        log_info "搜索 $lib 的头文件..."
        
        # 查找该库的构建目录
        local lib_dirs=$(find "$YOCTO_WORK_DIR" -type d -name "$lib*" 2>/dev/null | head -n 5)
        
        for lib_dir in $lib_dirs; do
            # 在多个可能的位置查找头文件
            local include_paths=(
                "$lib_dir/git"
                "$lib_dir/build"
                "$lib_dir/image/usr/include"
                "$lib_dir/sysroot-destdir/usr/include"
            )
            
            for include_path in "${include_paths[@]}"; do
                if [ -d "$include_path" ]; then
                    # 查找并复制头文件
                    while IFS= read -r -d '' header_file; do
                        # 计算相对路径
                        local rel_path="${header_file#$include_path/}"
                        local dest_dir="$HEADER_OUTPUT/$(dirname "$rel_path")"
                        
                        # 创建目标目录
                        mkdir -p "$dest_dir"
                        
                        # 复制头文件
                        if cp "$header_file" "$dest_dir/" 2>/dev/null; then
                            ((header_count++))
                        fi
                    done < <(find "$include_path" -type f \( -name "*.h" -o -name "*.hpp" \) -print0 2>/dev/null)
                fi
            done
        done
    done
    
    log_success "共提取 $header_count 个头文件"
    return 0
}

# 验证提取的文件
verify_libraries() {
    log_info "验证提取的库文件..."
    
    local valid_count=0
    local invalid_count=0
    
    # 清空库清单文件
    > "$INVENTORY_FILE"
    
    # 写入清单文件头部
    cat >> "$INVENTORY_FILE" << EOF
# OpenBMC库文件清单
# 生成时间: $(date '+%Y-%m-%d %H:%M:%S')
# ============================================================================
# 格式: 文件名 | 文件类型 | 大小 | 符号数量 | MD5
# ============================================================================

EOF
    
    for so_file in "$LIBRARY_OUTPUT"/*.so*; do
        if [ ! -f "$so_file" ]; then
            continue
        fi
        
        local filename=$(basename "$so_file")
        local filesize=$(stat -f%z "$so_file" 2>/dev/null || stat -c%s "$so_file" 2>/dev/null)
        local filetype=$(file -b "$so_file")
        local md5sum=$(md5sum "$so_file" 2>/dev/null | awk '{print $1}' || md5 -q "$so_file" 2>/dev/null)
        
        # 使用nm检查符号
        local symbol_count=0
        if nm -D "$so_file" &>/dev/null; then
            symbol_count=$(nm -D "$so_file" 2>/dev/null | wc -l | tr -d ' ')
            ((valid_count++))
            log_info "✓ $filename - $symbol_count 个符号"
        else
            log_error "✗ $filename - 无法读取符号"
            ((invalid_count++))
        fi
        
        # 写入清单
        printf "%-40s | %-30s | %10d | %8d | %s\n" \
            "$filename" "$filetype" "$filesize" "$symbol_count" "$md5sum" \
            >> "$INVENTORY_FILE"
    done
    
    # 写入统计信息
    cat >> "$INVENTORY_FILE" << EOF

# ============================================================================
# 统计信息
# ============================================================================
总计库文件: $((valid_count + invalid_count))
有效库文件: $valid_count
无效库文件: $invalid_count
生成时间: $(date '+%Y-%m-%d %H:%M:%S')
EOF
    
    log_success "验证完成 - 有效: $valid_count, 无效: $invalid_count"
    log_info "库清单已保存到: $INVENTORY_FILE"
}

# 显示提取摘要
show_summary() {
    log_info "========================================"
    log_info "提取摘要"
    log_info "========================================"
    log_info "库文件目录: $LIBRARY_OUTPUT"
    log_info "头文件目录: $HEADER_OUTPUT"
    log_info "库清单文件: $INVENTORY_FILE"
    log_info ""
    log_info "库文件数量: $(find "$LIBRARY_OUTPUT" -type f -name "*.so*" 2>/dev/null | wc -l | tr -d ' ')"
    log_info "头文件数量: $(find "$HEADER_OUTPUT" -type f \( -name "*.h" -o -name "*.hpp" \) 2>/dev/null | wc -l | tr -d ' ')"
    log_info "========================================"
    
    # 显示库清单的前10行
    if [ -f "$INVENTORY_FILE" ]; then
        log_info ""
        log_info "库清单预览（前10个）:"
        head -n 20 "$INVENTORY_FILE" | tail -n 10
    fi
}

# ============================================================================
# 主流程
# ============================================================================

main() {
    log_info "========================================"
    log_info "OpenBMC库文件提取脚本"
    log_info "========================================"
    
    # 准备输出目录
    prepare_directories
    
    # 提取.so文件
    extract_so_files
    
    # 提取头文件
    extract_header_files
    
    # 验证库文件
    verify_libraries
    
    # 显示摘要
    show_summary
    
    log_success "========================================"
    log_success "库文件提取完成！"
    log_success "========================================"
}

# 执行主流程
main "$@"

